/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-05
 * V4.0
 */
package com.jphenix.servlet.common;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//#endregion

import com.jphenix.servlet.multipart.instancea.DownloadFile;
import com.jphenix.servlet.multipart.instancea.MultipartServletRequest;
import com.jphenix.share.lang.SString;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.lang.ICloneable;
import com.jphenix.standard.servlet.IActionContext;
import com.jphenix.standard.servlet.api.ICookie;
import com.jphenix.standard.servlet.api.IRequest;
import com.jphenix.standard.servlet.api.IResponse;
import com.jphenix.standard.servlet.api.ServletOutputStream;

//#region 【说明区】
/**
 * 动作上下文
 * 
 * 2019-07-15 上传文件的根文件夹变为可配置的根文件夹
 * 2019-10-25 修改了因为传参为空，调用Tomcat方法抛空指针问题
 * 2020-03-13 增加了从URL中获取参数值的方法 getUrlParameter
 * 2022-09-04 隔离了ServletApi，兼容新老Tomcat
 * 2022-09-05 修改了发现的错误
 * 
 * 
 * @author 刘虻
 * 2010-5-22 下午11:37:51
 */
//#endregion
@ClassInfo({"2024-07-14 12:03","动作上下文"})
public class ActionContext implements IActionContext, ICloneable {

	//#region 【引用区】
	protected IRequest                req             = null; // 页面请求对象
	protected IResponse               resp            = null; // 页面反馈对象
	protected MultipartServletRequest msr             = null; // 非文本页面请求
	protected int                     multipartStatus = -1;   // 是否为非文本页面请求 -1未初始化 0不是 1是
	
	protected String                  uploadBasePath  = null; // 上传文件根路径
    protected String                  domain          = null; // 域名
    protected String                  action          = null; // 动作路径
	protected StringBuffer            errorMsgSbf     = new StringBuffer(); // 错误信息缓存
	//#endregion
	
	//#region 构造函数
	/**
	 * 构造函数
	 * @author 刘虻
	 * @param req 页面请求对象
	 * @param resp 页面反馈对象
	 * @param domain 访问域名
	 * @param action 动作路径
	 * @param uploadBasePath 上传根路径
	 */
	public ActionContext(IRequest req,IResponse resp,String domain,String action,String uploadBasePath) {
		super();
		this.req            = req;
		this.resp           = resp;
		this.action         = action;
		this.domain         = domain;
		this.uploadBasePath = uploadBasePath;
	}
	//#endregion
	
	//#region getErrorMessage() 获取错误信息
	/**
	 * 获取错误信息
	 * 刘虻
	 * 2010-6-17 下午02:56:22
	 * @return 错误信息
	 */
	@Override
    public String getErrorMessage() {
		return errorMsgSbf.toString();
	}
	//#endregion
	
	//#region isMultipartRequest() 是否为非文本信息提交
	/**
	 * 是否为非文本信息提交
	 * 刘虻
	 * 2011-5-4 下午04:29:44
	 * @return true是
	 */
	@Override
    public boolean isMultipartRequest() {
		if(req==null) {
			return false;
		}
		if(multipartStatus==-1) {
			if(req.getContentType()!=null 
					&& req.getContentType().toLowerCase().startsWith("multipart")) {
				multipartStatus = 1;
			}else {
				multipartStatus = 0;
			}
		}
		return multipartStatus == 1;
	}
	//#endregion
	
	//#region getMultipartServletRequest() 获取非文本信息请求对象
	/**
	 * 获取非文本信息请求对象
	 * 刘虻
	 * 2011-5-4 下午04:33:12
	 * @return 非文本信息请求对象
	 */
	@Override
    public MultipartServletRequest getMultipartServletRequest() {
		if(msr==null) {
			msr = new MultipartServletRequest(req,uploadBasePath,"/temp");
		}
		return msr;
	}
	//#endregion
	
	//#region addErrorMessage(content) 累加错误信息
	/**
	 * 累加错误信息
	 * 刘虻
	 * 2010-6-17 下午02:55:55
	 * @param content 错误信息
	 */
	@Override
    public void addErrorMessage(String content) {
		if(errorMsgSbf.length()>0) {
			errorMsgSbf.append(content);
		}
	}
	//#endregion
	
	//#region getAction() 获取动作路径
	/**
	 * 获取动作路径
	 * 刘虻
	 * 2010-6-9 下午08:49:55
	 * @return 动作路径
	 */
	@Override
    public String getAction() {
		if(action==null) {
			action = req.getServletPath();
			//去掉扩展名
			int point = action.indexOf(".");
			if(point>-1) {
				action = action.substring(0,point);
			}
		}
		return action;
	}
	//#endregion
	
	//#region getResponse() 获取页面反馈对象
	/**
	 * 获取页面反馈对象
	 * 刘虻
	 * 2010-5-22 下午11:42:24
	 * @return 页面反馈对象
	 */
	@Override
    public IResponse getResponse() {
		return resp;
	}
	//#endregion
	
	//#region setResponse(resp) 设置页面反馈对象
	/**
	 * 设置页面反馈对象
	 * 刘虻
	 * 2010-5-22 下午11:42:36
	 * @param resp 页面反馈对象
	 */
	public void setResponse(IResponse resp) {
		this.resp = resp;
	}
	//#endregion
	
	//#region getRequest() 获取页面请求对象
	/**
	 * 获取页面请求对象
	 * 刘虻
	 * 2010-5-22 下午11:42:41
	 * @return 页面请求对象
	 */
	@Override
    public IRequest getRequest() {
		return req;
	}
	//#endregion
	
	//#region setRequest(req) 设置页面请求对象
	/**
	 * 设置页面请求对象
	 * 刘虻
	 * 2010-5-22 下午11:42:50
	 * @param req 页面请求对象
	 */
	public void setRequest(IRequest req) {
		this.req = req;
	}
	//#endregion
	
	//#region clone()
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-5-22 下午11:42:56
	 */
	@Override
    public Object clone() {
		//构造返回值
		ActionContext ac = new ActionContext(req,resp,domain,action,uploadBasePath);
		ac.msr = msr;
		ac.multipartStatus = multipartStatus;
		return ac;
	}
	//#endregion
	
	//#region clear() 清除缓存
	/**
	 * 清除缓存
	 * 2017年4月27日
	 * @author MBG
	 */
	@Override
    public void clear() {
		if(resp!=null) {
			resp.clear();
		}
	}
	//#endregion

	//#region flush() 刷新缓存
    /**
     * 刷新缓存
     * @throws Exception 异常
     * 2014年8月28日
     * @author 马宝刚
     */
    @Override
    public void flush() throws Exception {
    	if(resp!=null) {
    		resp.flushBuffer();
    	}
    }
    //#endregion
    
    //#region getUrlParameter(key) 从URL中获取参数值
    /**
     * 从URL中获取参数值
     * @param key  参数主键
     * @return     对应的参数值
     * 2020年3月13日
     * @author MBG
     */
    public String getUrlParameter(String key) {
    	if(req==null) {
    		return "";
    	}
    	return req.getUrlParameter(key);
    }
    //#endregion
    
    //#region getStream() 获取输出流
    /**
     * 获取输出流
     * 2014年8月28日
     * @author 马宝刚
     */
    @Override
    public ServletOutputStream getStream() throws Exception {
    	if(resp!=null) {
    		return resp.iGetOutputStream();
    	}
    	return null;
    }
    //#endregion
    
    //#region write(contentBytes) 直接将信息输出到界面
    /**
     * 直接将信息输出到界面
     * @param contentBytes 信息
     * @throws Exception 异常
     * 2014年8月28日
     * @author 马宝刚
     */
    @Override
    public void write(byte[] contentBytes) throws Exception {
    	if(resp!=null && contentBytes!=null) {
    		resp.iGetOutputStream().write(contentBytes);
    	}
    }
	//#endregion
    
    //#region outContent(content) 直接将内容输出到页面中
    /**
     * 直接将内容输出到页面中
     * @param content 信息对象
     * @return 当前类实例
     * @throws Exception 异常
     * 2014年8月16日
     * @author 马宝刚
     */
    @Override
    public void outContent(Object content) throws Exception {
    	if(content!=null) {
    		if(resp!=null) {
    			resp.setCharacterEncoding("UTF-8");
            	if(content instanceof String) {
            		resp.iGetOutputStream().write(((String)content).getBytes(StandardCharsets.UTF_8));
            	}else if(content instanceof byte[]) {
            		resp.iGetOutputStream().write((byte[])content);
            	}else {
            		resp.iGetOutputStream().write(content.toString().getBytes(StandardCharsets.UTF_8));
            	}
    		}
    	}
    }
    //#endregion
    
    //#region getParameter(key) 获取参数值(参数值永不等于空)
	/**
	 * 获取参数值(参数值永不等于空)
	 * 刘虻
	 * 2010-6-9 下午05:39:24
	 * @param key 参数主键
	 * @return 参数值
	 */
	@Override
    public String getParameter(String key) {
		if(isMultipartRequest()) {
			return SString.valueOf(getMultipartServletRequest().getParameter(key));
		}
		return SString.valueOf(getRequest().getParameter(key));
	}
	//#endregion
	
	//#region setParameter(key,values) 设置参数值
    /**
     * 设置参数值
     * @param key          参数主键
     * @param values      参数值
     * 2014年6月12日
     * @author 马宝刚
     */
    @Override
    public void setParameter(String key, String[] values) {
        if(isMultipartRequest()) {
                getMultipartServletRequest().setParameter(key,values);
        }else {
                getRequest().setParameter(key,values);
        }
    }
    //#endregion
    
    //#region hasParameter(key) 是否存在该参数
    /**
     * 是否存在该参数
     * @param key 参数主键
     * @return 是否存在该参数
     * 2014年6月12日
     * @author 马宝刚
     */
    @Override
    public boolean hasParameter(String key) {
        if(isMultipartRequest()) {
            return getMultipartServletRequest().getParameterMap().containsKey(key);
        }
        return getRequest().getParameterMap().containsKey(key);
    }
    //#endregion

    //#region getSingleParameterMap() 获取参数容器
	/**
	 * 获取参数容器
	 * 原getParameterMap中的参数值为字符串数组，用起来不方便
	 * 刘虻
	 * 2010-7-6 下午03:38:43
	 * @return 参数容器
	 */
	@Override
    public Map<String,String> getSingleParameterMap() {
		//构建返回值
		HashMap<String,String> reMap = new HashMap<String,String>();
		
		Map<String,String[]> parameterMap; //提交参数信息容器
		if(isMultipartRequest()) {
			parameterMap = getMultipartServletRequest().getParameterMap();
		}else {
			parameterMap = req.getParameterMap();
		}
		//获取参数主键迭代
		List<String> keyList = BaseUtil.getMapKeyList(parameterMap);
		String[] values; //参数值
		for(String key:keyList) {
			if(key==null || key.length()<1) {
				continue;
			}
			values = parameterMap.get(key);
			if(values==null || values.length<1) {
				reMap.put(key,null);
			}else {
				reMap.put(key,values[0]);
			}
		}
		return reMap;
	}
	//#endregion
	
	//#region getSessionAttribute(key) 获取会话属性信息
	/**
	 * 获取会话属性信息
	 * 刘虻
	 * 2010-6-9 下午05:38:03
	 * @param key 会话属性主键
	 * @return 会话属性值
	 */
	@Override
    public Object getSessionAttribute(String key) {
		return getRequest().iGetSession().getAttribute(key);
	}
	//#endregion

	//#region setSessionAttribute(key,value) 设置会话属性信息
	/**
	 * 设置会话属性信息
	 * 刘虻
	 * 2010-6-9 下午05:38:25
	 * @param key 会话属性信息主键
	 * @param value 会话属性信息值
	 */
	@Override
    public void setSessionAttribute(String key, Object value) {
		getRequest().iGetSession().setAttribute(key,value);
	}
	//#endregion
	
	//#region sessionInvalidate() 销毁用户会话信息
	/**
	 * 销毁用户会话信息
	 * 2024年7月14日
	 * @author MBG
	 */
	@Override
	public void sessionInvalidate() {
		getRequest().iGetSession().invalidate();
	}
	//#endregion
	
	//#region remoteSessionAttribute(key) 从用户会话容器中移除指定key
	/**
	 * 从用户会话容器中移除指定key
	 * @param key     指定的key
	 * @return        被移除的指定的key的值b
	 * 2024年7月14日
	 * @author MBG
	 */
	@Override
	public void remoteSessionAttribute(String key) {
		getRequest().iGetSession().removeAttribute(key);
	}
	//#endregion
	
	//#region getSessionID() 获取会话主键
	/**
	 * 获取会话主键
	 * 刘虻
	 * 2010-6-9 下午05:38:44
	 * @return 会话主键
	 */
	@Override
    public String getSessionID() {
		return getRequest().iGetSession().getId();
	}
	//#endregion
	
	//#region getAttribute(key)
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-6-9 下午05:35:57
	 */
	@Override
    public Object getAttribute(String key) {
		return getRequest().getAttribute(key);
	}
	//#endregion

	//#region setAttribute(key,value) 
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-6-9 下午05:35:53
	 */
	@Override
    public void setAttribute(String key, Object value) {
		getRequest().setAttribute(key,value);
	}
	//#endregion
	
	//#region setHeader(key,value) 设置返回报文头信息
    /**
     * 设置返回报文头信息
     * @param key 信息主键
     * @param value 信息值
     * 2014年8月28日
     * @author 马宝刚
     */
    @Override
    public void setHeader(String key, String value) {
    	if(resp!=null) {
    		resp.setHeader(key,value);
    	}
    }
    //#endregion
	
    //#region addHeader(key,value) 增加报文头信息
    /**
     * 增加报文头信息
     * @param key    信息主键
     * @param value 信息值
     * 2017年4月28日
     * @author MBG
     */
    @Override
    public void addHeader(String key, String value) {
    	if(resp!=null) {
    		resp.addHeader(key,value);
    	}
    }
    //#endregion

    //#region getWebBasePath()
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2012-7-29 下午10:09:13
	 */
	@Override
    public String getWebBasePath() {
		return req.getWebBasePath();
	}
	//#endregion
	
	//#region getDomain() 
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2012-7-30 上午8:55:38
	 */
	@Override
    public String getDomain() {
		if(domain==null) {
			if(req==null) {
				domain = "";
			}else {
				domain =  req.getServerName().toLowerCase();
			}
		}
		return domain;
	}
	//#endregion
	
	//#region setCookie(key,value) 设置Cookie信息（默认最长时间，全局域）
	/**
	 * 设置Cookie信息（默认最长时间，全局域）
	 * @param key      主键
	 * @param value    值
	 * 2014年5月28日
	 * @author 马宝刚
	 */
	@Override
    public void setCookie(String key, String value) {
		setCookie(key,value,null,0);
	}
	//#endregion
	
	//#region setCookie(key,value,path,outTime) 设置Cookie信息（默认最长时间1年，全局域）
	/**
	 * 设置Cookie信息（默认最长时间1年，全局域）
	 * @param key          主键
	 * @param value       值
	 * @param path        域（相对路径）
	 * @param outTime  过期时间（秒）
	 * 2017年8月21日
	 * @author MBG
	 */
	@Override
    public void setCookie(String key, String value, String path, int outTime) {
	    if(key==null || key.length()<1) {
	        return;
	    }
	    if(value==null) {
	        value = "";
	    }
	    if(outTime<1) {
	    	outTime = 31536000; //一年
	    }
	    if(path==null || path.length()<1) {
	    	path = "/";
	    }
	    //构建Cookie
	    ICookie cookie = resp.createCookie(key,value);
	    cookie.setPath(path);
	    cookie.setMaxAge(outTime); //两周
	    
	    getResponse().addCookie(cookie);
	}
	//#endregion
	
	//#region getCookie(key) 获取Cookie值
	/**
	 * 获取Cookie值
	 * @param key Cookie主键
	 * @return Cookie值
	 * 2014年5月28日
	 * @author 马宝刚
	 */
	@Override
    public String getCookie(String key) {
	    if(key==null) {
	        return "";
	    }
	    //获取全部cookie信息
	    ICookie[] cookies = getRequest().iGetCookies();
	    if(cookies==null) {
	    	return "";
	    }
	    for(int i=0;i<cookies.length;i++) {
	    	if(cookies[i]==null) {
	    		continue;
	    	}
	        if(key.equals(cookies[i].getName())) {
	            return SString.valueOf(cookies[i].getValue());
	        }
	    }
	    return "";
	}
	//#endregion
	
	//#region delCookie(key) 删除指定Cookie
	/**
	 * 删除指定Cookie
	 * @param key 主键
	 * 2017年9月11日
	 * @author MBG
	 */
	@Override
    public void delCookie(String key) {
	    //构建Cookie
	    ICookie cookie = resp.createCookie(key,null);
	    cookie.setMaxAge(0); //马上失效
	    getResponse().addCookie(cookie);
	}
	//#endregion
	
	//#region delCookie(key,path) 删除指定Cookie
	/**
	 * 删除指定Cookie
	 * @param key  主键
	 * @param path 域（相对路径）
	 * 2017年9月11日
	 * @author MBG
	 */
	@Override
    public void delCookie(String key, String path) {
	    //构建Cookie
	    ICookie cookie = resp.createCookie(key,null);
	    cookie.setMaxAge(0); //马上失效
	    if(path!=null) {
	    	cookie.setPath(path);
	    }
	    getResponse().addCookie(cookie);
	}
	//#endregion
	
	//#region getDownloadFile() 获取下载处理对象
	/**
	 * 获取下载处理对象
	 * @return 下载处理对象
	 * 2014年8月17日
	 * @author 马宝刚
	 */
	@Override
    public DownloadFile getDownloadFile() {
		return new DownloadFile(resp,uploadBasePath);
	}
	//#endregion
	
	//#region getDownloadFile(uploadBasePath) 获取下载处理对象
	/**
	 * 获取下载处理对象
	 * @param uploadBasePath 重新设置的文件根路径
	 * @return 下载处理对象
	 * 2016年11月14日
	 * @author MBG
	 */
	@Override
    public DownloadFile getDownloadFile(String uploadBasePath) {
		if(uploadBasePath==null || uploadBasePath.trim().length()<1) {
			uploadBasePath = this.uploadBasePath;
		}
		return new DownloadFile(resp,uploadBasePath);
	}
	//#endregion
	
	//#region getRemoteAddress() 获取客户端IP地址
	/**
	 * 获取客户端IP地址
	 * @return 客户端IP地址
	 * 2016年9月28日
	 * @author MBG
	 */
	@Override
    public String getRemoteAddress() {
		return req.getRemoteAddr();
	}
	//#endregion
	
	//#region getRemoteHost() 获取客户端域名
	/**
	 * 获取客户端域名
	 * @return 客户端域名
	 * 2016年9月28日
	 * @author MBG
	 */
	@Override
    public String getRemoteHost() {
		return req.getRemoteHost();
	}
	//#endregion
	
	//#region cip() 获取发起请求的服务器的IP地址（避免取到代理服务器地址）
	/**
	 * 获取发起请求的服务器的IP地址（避免取到代理服务器地址）
	 * @return 发起请求的服务器的IP地址（返回信息绝对不为null）
	 * 2016年10月17日
	 * @author MBG
	 */
	@Override
    public String cip() {
		return req.cip();
	}
	//#endregion
	
	//#region uploadPath() 上传文件根路径（绝对路径）
	/**
	 * 上传文件根路径（绝对路径）
	 * @return 上传文件根路径
	 * 2016年11月3日
	 * @author MBG
	 */
	@Override
    public String uploadPath() {
		return uploadBasePath;
	}
	//#endregion
	
	//#region toString()
	/**
	 * 覆盖方法
	 */
	@Override
    public String toString() {
		//构建返回值
		String reStr = getClass().getName()+"_"+hashCode();
		if(req!=null) {
			reStr += "\n"+req;
		}
		return reStr;
	}
	//#endregion
	
	//#region isPost() 请求方式是否为POST
	/**
	 * 请求方式是否为POST
	 * @return 请求方式是否为POST
	 * 2017年5月1日
	 * @author MBG
	 */
	@Override
    public boolean isPost() {
		if(req!=null) {
			return req.isPost();
		}
		return false;
	}
	//#endregion
}










